pub mod plist;
pub mod reader;
pub mod xml;

mod glyph;
mod group;
mod info;
mod kern;
mod layer;
mod meta;
mod parser;

pub use glyph::*;
pub use group::*;
pub use info::*;
pub use kern::*;
pub use layer::*;
pub use meta::*;
pub use parser::*;

use crate::fea::Fea;

#[derive(Debug, Clone)]
pub struct Ufo {
	pub base: std::path::PathBuf,
	pub meta: MetaInfo,
	pub info: FontInfo,
	pub layers: Layers,
	pub groups: Groups,
	pub kern: Kerning,
	pub features: Fea,
	pub lib: Value,
}

impl Ufo {
	pub fn load(path: impl AsRef<std::path::Path>) -> Result<Self, Error> {
		use std::fs::read_to_string;
		let path = path.as_ref();
		let meta = {
			let content = read_to_string(path.join(METAINFO_FILE)).map_err(Error::from)?;
			let mut xml = xml::Parser::new(&content);
			MetaInfo::from_plist(&mut Parser(&mut plist::Parser::new(&mut xml).peekable()))?
		};
		let mut this = Self {
			base: path.to_path_buf(),
			layers: if let Ok(content) = read_to_string(path.join(LAYER_CONTENTS_FILE)) {
				let mut xml = xml::Parser::new(&content);
				Layers::parse(&mut Parser(&mut plist::Parser::new(&mut xml).peekable()), path)?
			} else {
				if meta.format_version == 3 {
					return Err(Error::Msg("Missing Layer Contents File"));
				}
				Default::default()
			},
			info: if let Ok(content) = read_to_string(path.join(FONTINFO_FILE)) {
				let mut xml = xml::Parser::new(&content);
				FontInfo::from_plist(&mut Parser(&mut plist::Parser::new(&mut xml).peekable()))?
			} else {
				Default::default()
			},
			groups: if let Ok(content) = read_to_string(path.join(GROUPS_FILE)) {
				let mut xml = xml::Parser::new(&content);
				Groups::parse(&mut Parser(&mut plist::Parser::new(&mut xml).peekable()))?
			} else {
				Default::default()
			},
			kern: if let Ok(content) = read_to_string(path.join(KERNING_FILE)) {
				let mut xml = xml::Parser::new(&content);
				Kerning::parse(&mut Parser(&mut plist::Parser::new(&mut xml).peekable()))?
			} else {
				Default::default()
			},
			features: if let Ok(content) = read_to_string(path.join(FEATURES_FILE)) {
				Fea::new(&path, &content).map_err(|e| Error::Fea(format!("{:?}", e)))?
			} else {
				Default::default()
			},
			lib: if let Ok(content) = read_to_string(path.join(LIB_FILE)) {
				let mut xml = xml::Parser::new(&content);
				Value::from_plist(&mut Parser(&mut plist::Parser::new(&mut xml).peekable()))?
			} else {
				Default::default()
			},
			meta,
		};
		this.layers.sure_default(path)?;
		Ok(this)
	}
	pub fn load_glyph(&self, layer: Option<&str>, name: &str) -> Option<std::path::PathBuf> {
		let layer = layer.unwrap_or(DEFAULT_LAYER_NAME);
		let layer = self.layers.get(layer)?;
		let path = layer.contents.get(name)?;
		Some(path.clone())
	}
	pub fn layer(&self, layer: Option<&str>) -> Option<&Layer> {
		self.layers.get(layer.unwrap_or(DEFAULT_LAYER_NAME))
	}
}

pub static METAINFO_FILE: &str = "metainfo.plist";
pub static FONTINFO_FILE: &str = "fontinfo.plist";
pub static LIB_FILE: &str = "lib.plist";
pub static GROUPS_FILE: &str = "groups.plist";
pub static KERNING_FILE: &str = "kerning.plist";
pub static FEATURES_FILE: &str = "features.fea";
pub static LAYER_CONTENTS_FILE: &str = "layercontents.plist";

pub static DATA_DIR: &str = "data";
pub static IMAGES_DIR: &str = "images";
pub static DEFAULT_GLYPHS_DIR: &str = "glyphs";

pub static CONTENTS_FILE: &str = "contents.plist";
pub static LAYER_INFO_FILE: &str = "layerinfo.plist";

pub static DEFAULT_METAINFO_CREATOR: &str = "org.linebender.norad";
pub static DEFAULT_LAYER_NAME: &str = "public.default";

#[cfg(test)]
mod tests {
	use super::*;
	#[test]
	fn load_fontinfotest() {
		let ufo = Ufo::load("assets/ufo/fontinfotest.ufo").unwrap();
		println!("{:#?}", ufo);
		for (_, path) in ufo.layer(None).unwrap().contents.iter() {
			let content = std::fs::read_to_string(path).unwrap();
			let glyph = Glyph::parse(&mut xml::Parser::new(&content)).unwrap();
			println!("{:#?}", glyph);
		}
	}
	#[test]
	fn load_fontinfotest_v3() {
		let ufo = Ufo::load("assets/ufo/fontinfotest_v3.ufo").unwrap();
		println!("{:#?}", ufo);
	}
	#[test]
	fn load_fontinfotest_v2() {
		let ufo = Ufo::load("assets/ufo/fontinfotest_v2.ufo").unwrap();
		println!("{:#?}", ufo);
	}
	#[test]
	fn load_fontinfotest_v1() {
		let ufo = Ufo::load("assets/ufo/fontinfotest_v1.ufo").unwrap();
		println!("{:#?}", ufo);
	}
}
